package com.codeyang.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codeyang.constant.QueueConstant;
import com.codeyang.domain.*;
import com.codeyang.dto.OrderConfirmParam;
import com.codeyang.feign.OrderCartFeign;
import com.codeyang.feign.OrderProdFeign;
import com.codeyang.feign.OrderUserAddrFeign;
import com.codeyang.mapper.OrderMapper;
import com.codeyang.model.WxMsgModel;
import com.codeyang.service.OrderItemService;
import com.codeyang.service.OrderService;
import com.codeyang.vo.OrderResult;
import com.codeyang.vo.OrderStatusModel;
import com.codeyang.vo.ShopOrderItem;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 描述: TO-删除标志-DO
 *
 * @author CodeYang_Site
 * @version 2021/5/31 17:42
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderUserAddrFeign orderUserAddrFeign;

    @Autowired
    private OrderProdFeign orderProdFeign;

    @Autowired
    private OrderCartFeign orderCartFeign;

    @Autowired
    private Snowflake snowflake;
    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 根据用户id查询用户订单状态 数量合集
     *
     * @param userId
     * @return
     */
    @Override
    public OrderStatusModel findUserOrderStatusCount(String userId) {
        //1.确定返回对象
        OrderStatusModel orderStatusModel = new OrderStatusModel();
        //2.直接 userId 全查询
        List<Order> orders = orderMapper.selectList(new LambdaQueryWrapper<Order>()
                .eq(Order::getUserId, userId)
        );
        //3.使用stream 过滤数据
        if (!CollectionUtils.isEmpty(orders)) {
            //过滤除不同状态的数据 订单数据统计数
            int unPay = orders.stream().filter(order -> order.getStatus().equals(1)).collect(Collectors.toList()).size();
            int payed = orders.stream().filter(order -> order.getStatus().equals(2)).collect(Collectors.toList()).size();
            int consignment = orders.stream().filter(order -> order.getStatus().equals(3)).collect(Collectors.toList()).size();
            orderStatusModel.setUnPay(unPay);
            orderStatusModel.setPayed(payed);
            orderStatusModel.setConsignment(consignment);
        }
        return orderStatusModel;
    }

    /**
     * 分页查询用户的订单
     * step:
     * 1.分页查询订单
     * 2.查询订单条目集合
     * 3.筛选组装
     *
     * @param page
     * @param order
     * @return
     */
    @Override
    public Page<Order> findMyOrderByPage(Page<Order> page, Order order) {
        // 状态 0 是全查询
        Page<Order> orderPage = orderMapper.selectPage(page, new LambdaQueryWrapper<Order>()
                .eq(Order::getUserId, order.getUserId())
                .eq(null != order.getStatus() && !order.getStatus().equals(0), Order::getStatus, order.getStatus())
                .orderByDesc(Order::getCreateTime)
        );
        List<Order> orderList = orderPage.getRecords();

        if (!CollectionUtils.isEmpty(orderList)) {
            //收集数据 得到订单条目编号
            List<String> orderItemNumbers = orderList.stream().map(Order::getOrderNumber).collect(Collectors.toList());
            //查询订单条目表 in 查询
            List<OrderItem> orderItems = orderItemService.list(new LambdaQueryWrapper<OrderItem>().in(OrderItem::getOrderNumber, orderItemNumbers));

            //组装数据
            orderList.forEach(order1 -> {
                List<OrderItem> itemList = orderItems
                        .stream()
                        .filter(orderItem ->
                                orderItem.getOrderNumber().equals(order1.getOrderNumber()))
                        .collect(Collectors.toList());
                //放入
                // 订单商品总数
                // TODO 注意这里的商品总数,如果在插入订单表时,这个冗余字段数据没有放进去,那么就有问题
                order1.setProductNums(itemList.size());
                order1.setOrderItemDtos(itemList);
            });
        }
        return orderPage;
    }

    /**
     * 确认订单信息 回显
     * 两种路径:
     * step:
     * 1.从商品直接进入确认 传输对象 OrderItem
     * 2.从购物车进入 List<basketIds>
     * 3.组装返回数据
     *
     * @param userId
     * @param orderConfirmParam
     * @return
     */
    @Override
    public OrderResult confirmOrder(String userId, OrderConfirmParam orderConfirmParam) {
        //1.return
        OrderResult orderResult = new OrderResult();
        //组装一些 必要返回参数
        //2. 用户默认的收货地址
        UserAddr userAddr = orderUserAddrFeign.getUserDefaultAddr(userId);
        orderResult.setUserAddr(userAddr);
        //3.判断是那种路径进入确认页面
        List<Long> basketIds = orderConfirmParam.getBasketIds();
        if (CollectionUtils.isEmpty(basketIds)) {
            //直接从商品进入确认
            prodToConfirm(orderResult, orderConfirmParam.getOrderItem());
        } else {

            // 从购物车进来确认
            cartToConfirm(orderResult, basketIds);
        }


        return orderResult;
    }

    /**
     * 从购物车进入确认
     * step:
     * 1.根据basketIds 查询购物车 得到购物车数据集合  -- 直接远程调用
     * 2.拿到skuids
     * 3.远程调用 根据skuIds 得到 skus 集合
     * 4.循环购物车 计算总金额,   数量
     * 5.组装数据
     *
     * @param orderResult
     * @param basketIds
     */
    private void cartToConfirm(OrderResult orderResult, List<Long> basketIds) {
        //1.
        List<Basket> baskets = orderCartFeign.getBasketsByIds(basketIds);
        if (CollectionUtils.isEmpty(baskets)) {
            throw new RuntimeException("远程调用 getBasketsByIds 查询失败");
        }
        //2.
        List<Long> skuIds = baskets.stream().map(Basket::getSkuId).collect(Collectors.toList());
        //3.
        List<Sku> skus = orderProdFeign.getSkusBySkuIds(skuIds);
        if (CollectionUtils.isEmpty(skus)) {
            throw new RuntimeException("远程调用 getSkusBySkuIds 查询失败");
        }
        //4.
        // 创建总金额的集合 因为这里的 sku 数量不只一个了
        List<BigDecimal> totalMoneyList = new ArrayList<>();
        // 创建商品集合
        List<OrderItem> orderItems = new ArrayList<>();
        //循环组装数据
        baskets.forEach(basket -> {
            Sku sku1 = skus.stream().filter(sku -> sku.getSkuId().equals(basket.getSkuId())).collect(Collectors.toList()).get(0);
            //计算单个商品的总价
            BigDecimal oneTotalMoney = sku1.getPrice().multiply(new BigDecimal(basket.getBasketCount()));
            totalMoneyList.add(oneTotalMoney);

            //创建商品对象 copy 属性进入
            OrderItem orderItem = new OrderItem();
            BeanUtil.copyProperties(sku1, orderItem, true);
            BeanUtil.copyProperties(basket, orderItem, true);
            //设置数量
            orderItem.setProdCount(basket.getBasketCount());
            //设置单品总价
            orderItem.setProductTotalAmount(oneTotalMoney);
            orderItems.add(orderItem);
        });

        // 计算总金额 最终订单总金额
        BigDecimal totalMoney = totalMoneyList.stream()
                .reduce(BigDecimal::add)
                .get();
        // 计算最终总数量
        Integer totalCount = baskets.stream()
                .map(Basket::getBasketCount)
                .reduce(Integer::sum)
                .get();
        if (totalMoney.compareTo(new BigDecimal(99)) < 0) {
            // 设置运费
            orderResult.setTransfee(new BigDecimal(6));
            orderResult.setActualTotal(totalMoney.add(new BigDecimal(6)));
        } else {
            orderResult.setActualTotal(totalMoney);
        }
        //设置总金额
        orderResult.setTotal(totalMoney);
        // 设置总数量
        orderResult.setTotalCount(totalCount);
        // 创建商家对象
        ShopOrderItem shopOrderItem = new ShopOrderItem();
        // 设置商品集合到商家对象中去
        shopOrderItem.setShopCartItemDiscounts(orderItems);
        // 创建商家集合
        List<ShopOrderItem> shopOrderItems = new ArrayList<>();
        // 添加商家对象到商家集合中
        shopOrderItems.add(shopOrderItem);
        //最终返回对象设置进入
        orderResult.setShopCartOrders(shopOrderItems);

    }

    /**
     * 从商品进入
     *
     * @param orderResult 需要修改的引用对象
     * @param orderItem   传入参数
     */
    private void prodToConfirm(OrderResult orderResult, OrderItem orderItem) {
        //1.拿到skuid
        Long skuId = orderItem.getSkuId();
        //2.拿到商品数量
        Integer prodCount = orderItem.getProdCount();
        //3.远程调用 根据skuId,查询sku对象
        List<Sku> skus = orderProdFeign.getSkusBySkuIds(Arrays.asList(skuId));
        //4.远程调用必须判空
        if (CollectionUtils.isEmpty(skus)) {
            throw new RuntimeException("远程调用,根据skuIds 查询Sku集合失败");
        }
        // 单个商品,只有一个sku
        Sku sku = skus.get(0);
        //5.组装该数据

        //1.total总金额
        BigDecimal totalMoney = sku.getOriPrice().multiply(new BigDecimal(prodCount));
        //2.判断是否需要加运费 < 99 不需要
        if (totalMoney.compareTo(new BigDecimal(99)) < 0) {
            orderResult.setActualTotal(totalMoney.add(new BigDecimal(6)));
            orderResult.setTransfee(new BigDecimal(6));
        } else {
            orderResult.setActualTotal(totalMoney);
        }
        //设置总金额,总数量
        orderResult.setTotal(totalMoney);
        orderResult.setTotalCount(prodCount);
        // 创建商家集合
        List<ShopOrderItem> shopOrderItems = new ArrayList<>();
        // 创建 商家商品对象
        ShopOrderItem shopOrderItem = new ShopOrderItem();
        // 拷贝商品信息到 最终返回对象中
        BeanUtil.copyProperties(sku, orderItem, true);
        //订单条目商品总金额
        orderItem.setProductTotalAmount(totalMoney);
        //创建商品信息集合
        List<OrderItem> orderItems = new ArrayList<>();
        // 添加商品信息到商品信息集合中
        orderItems.add(orderItem);

        //设置商品信息集合到商家对象中
        shopOrderItem.setShopCartItemDiscounts(orderItems);
        // 添加商家对象到商家集合中
        shopOrderItems.add(shopOrderItem);
        //设置商家集合到返回结果中
        orderResult.setShopCartOrders(shopOrderItems);
        //倒叙设置,需要扫描就new 什么
    }


    /**
     * 生成预订单,准备支付,一系列工作
     * 1.生成订单号
     * 2.减库存
     * 3.清空购物车
     * 4.写死信队列--------->30分钟未支付,反向回滚操作
     * 5.写订单表 staus
     * 6.给用户发消息
     * 7.加用户积分
     * Transactional 自身调用的方法 添加事务 是无效的
     * 因为事务的本身是一个aop+代理 其调用方法前面的this不是原来的自己
     *
     * @param orderResult
     * @param userId
     * @return
     */
    @Transactional
    @Override
    public String createOrderToPay(OrderResult orderResult, String userId) {
        //1. 生成订单号
        String orderNumber = createOrderNumSnowflake();
        //2.清空购物车
        clearCart(userId, orderResult);
        //3减库存 减mysql  直接拿到一个返回的对象
        Map<Long, Integer> map = decrStockMysql(orderResult);
        //4 减少es的库存
        decrEsStock(map);
        //5写订单表
        Map<String, Object> resultMap = writeOrder(userId, orderNumber, orderResult);
        //6.写死信队列
        writeDeadQueue(orderNumber);
        //7.给用户发消息 -- 微信消息
        sendMsg(userId, resultMap);

        return orderNumber;
    }

    /**
     * 写订单表
     *
     * @param userId
     * @param resultMap
     */
    private void sendMsg(String userId, Map<String, Object> resultMap) {
        // 这里暂时是测试号,拿不到真正的openid 直接写死测试号的id oz_K86rTjHvsOS4pu4cUbHz9K6_w
        String prodName = (String) resultMap.get("prodName");
        String price = (String) resultMap.get("price");
        // 1.组装消息
        WxMsgModel wxMsgModel = new WxMsgModel();
        wxMsgModel.setUrl("https://www.baidu.com"); // 设置详情地址
        wxMsgModel.setTopColor("#173177");
        // 设置userid
        wxMsgModel.setToUser("oz_K86rTjHvsOS4pu4cUbHz9K6_w");
        wxMsgModel.setTemplateId("XazqNU4dtuXiVSDGebpC9bykQwDsN3kyK40DNWICohQ"); //设置模板

        HashMap<String, Map<String, String>> data = new HashMap<>();
        data.put("user", WxMsgModel.builderData(userId, "#173177"));
        data.put("number", WxMsgModel.builderData("4556789907622", "#173177"));
        DateTimeFormatter dfDateTime = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        LocalDateTime dateTime = LocalDateTime.now();
        String formatTime = dfDateTime.format(dateTime);
        data.put("buyTime", WxMsgModel.builderData(formatTime, "#173177"));
        data.put("product", WxMsgModel.builderData(prodName, "#173177"));
        data.put("time", WxMsgModel.builderData(formatTime, "#173177"));
        data.put("money", WxMsgModel.builderData(price, "#173177"));
        wxMsgModel.setData(data);
        // 发送消息给MQ 有消息监听 帮我们发送
        rabbitTemplate.convertAndSend(QueueConstant.WECHAT_MSG_QUEUE, JSON.toJSONString(wxMsgModel));
    }

    /**
     * 给用户推送消息
     *
     * @param userId
     * @param orderResult
     */


    /**
     * 写死信队列
     * 发给延迟队列
     *
     * @param orderNumber
     */
    private void writeDeadQueue(String orderNumber) {
        // 发给延迟队列, 监听死信队列
        rabbitTemplate.convertAndSend(QueueConstant.ORDER_MS_QUEUE, orderNumber);

    }

    /**
     * 写订单表
     * 写order表  名字 用，分割  价格（不能用前台的价格）  买家备注  状态  用户收货地址  该订单商品总数  是否支付
     * 和orderItem表
     *
     * @param userId
     * @param orderNumber
     * @param orderResult
     */
    private Map<String, Object> writeOrder(String userId, String orderNumber, OrderResult orderResult) {
        //1.拿skuIds
        ArrayList<Long> skuIds = new ArrayList<>();

        orderResult.getShopCartOrders().forEach(shopOrderItem -> {
            List<Long> shopSkuIds = shopOrderItem.getShopCartItemDiscounts()
                    .stream()
                    .map(OrderItem::getSkuId)
                    .collect(Collectors.toList());
            skuIds.addAll(shopSkuIds);
        });
        //2.根据skuIds 拿skus信息
        //远程调用 获取到skus
        List<Sku> skus = orderProdFeign.getSkusBySkuIds(skuIds);
        if (CollectionUtils.isEmpty(skus)) {
            throw new RuntimeException("服务器维护中");
        }
        //先写OrderItem表
        // 做名字拼接   name1,name2
        StringBuffer allOrderNames = new StringBuffer();


        //商品条目集合
        ArrayList<OrderItem> orderItems = new ArrayList<>();
        //商品订单的条目的 总价格集合
        ArrayList<BigDecimal> totalMoneyList = new ArrayList<>();
        // 循环设置 orderItem
        StringBuffer finalAllOrderNames = allOrderNames;
        orderResult.getShopCartOrders().forEach(shopOrderItem -> {

            shopOrderItem.getShopCartItemDiscounts().forEach(orderItem -> {
                //组装条目数据
                orderItem.setOrderNumber(orderNumber);
                orderItem.setUserId(userId);
                orderItem.setShopId(1L);
                orderItem.setRecTime(new Date());
                orderItem.setCommSts(0);
                // 处理真正的价格
                Sku sku1 = skus.stream()
                        .filter(sku -> sku.getSkuId().equals(orderItem.getSkuId()))
                        .collect(Collectors.toList()).get(0);
                // 处理单个商品的价格
                BigDecimal oneMoney = sku1.getPrice().multiply(new BigDecimal(orderItem.getProdCount()));
                orderItem.setProductTotalAmount(oneMoney);
                orderItem.setPrice(sku1.getPrice());

                //名字处理
                String prodName = orderItem.getProdName();
                finalAllOrderNames.append(prodName);
                finalAllOrderNames.append(",");
                //金额处理加入
                totalMoneyList.add(oneMoney);
                //处理skuId
                skuIds.add(orderItem.getSkuId());
                //处理订单条目
                orderItems.add(orderItem);
                //方便到外面去处理数据合集
            });
        });
        allOrderNames = finalAllOrderNames;
        //3.insert DB orderItems
        orderItemService.saveBatch(orderItems);
        //减掉拼接名字的最后一个逗号 name1,name2,
        allOrderNames = allOrderNames.deleteCharAt(allOrderNames.length() - 1);

        //4.处理Order表数据
        Order order = new Order();
        order.setUserId(userId);
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        order.setIsPayed(0);
        order.setRemarks(orderResult.getRemarks());
        order.setStatus(1); //设置支付状态
        order.setShopId(1L);
        order.setOrderNumber(orderNumber);
        order.setProductNums(orderResult.getTotalCount());
        order.setAddrOrderId(orderResult.getUserAddr().getAddrId());
        order.setProdName(allOrderNames.toString());

        //价格累加操作
        BigDecimal totalMoney = totalMoneyList.stream().reduce(BigDecimal::add).get();
        //判断运费是否需要计算
        if (totalMoney.compareTo(new BigDecimal(99)) < 0) {
            // 最终金额+6
            order.setActualTotal(totalMoney.add(new BigDecimal(6)));
        } else {
            order.setActualTotal(totalMoney);
        }
        order.setTotal(totalMoney);
        //插入 DB order
        orderMapper.insert(order);
        // 返回一个map
        Map<String, Object> map = new HashMap<>();
        map.put("prodName", allOrderNames.toString());
        map.put("price", totalMoney.toString());
        return map;

    }

    /**
     * 修改Es库存
     * ----
     * 发送到 监听Es的队列
     *
     * @param map
     */
    private void decrEsStock(Map<Long, Integer> map) {
        rabbitTemplate.convertAndSend(QueueConstant.PROD_CHANGE_EX, QueueConstant.PROD_CHANGE_KEY, JSON.toJSON(map));
    }

    /**
     * 减少mysql的库存
     * 要拿到 prodids ---> count
     * 一个 prodId 对应多个skuId
     * 1
     * 11 22
     * 要拿到 skuids ---> count
     *
     * @param orderResult
     */
    private Map<Long, Integer> decrStockMysql(OrderResult orderResult) {
        // 这里的这个 收集的 prodIdsMap 正好可以用来修改es库存集合
        Map<Long, Integer> prodIdsMap = new HashMap<>();
        Map<Long, Integer> skuIdsMap = new HashMap<>();
        //拿到店家集合
        List<ShopOrderItem> shopCartOrders = orderResult.getShopCartOrders();
        shopCartOrders.forEach(shopOrderItem -> {
            //拿到商品集合
            List<OrderItem> orderItems = shopOrderItem.getShopCartItemDiscounts();
            //循环遍历
            orderItems.forEach(orderItem -> {
                // 使用 + - 值来做 减少操作
                Integer count = orderItem.getProdCount() * -1;
                Long prodId = orderItem.getProdId();
                Long skuId = orderItem.getSkuId();
                skuIdsMap.put(skuId, count);
                // 判断一下这个 proid是否已经出现过了,如果出现了 就是累加操作
                if (prodIdsMap.containsKey(prodId)) {
                    prodIdsMap.put(prodId, prodIdsMap.get(prodId) + count);
                } else {
                    prodIdsMap.put(prodId, count);
                }
            });
        });
        //远程调用减库存
        // 这里post 可以直接接收两个对象吗 | 一般是一个
        Map<String, Map<Long, Integer>> data = new HashMap<>(4);
        data.put("prod", prodIdsMap);
        data.put("sku", skuIdsMap);
        orderProdFeign.changeStock(data);
        return prodIdsMap;


    }

    /**
     * 清空购物车
     * ctrl+alt+u 查看内关系图谱
     *
     * @param userId
     * @param orderResult
     */
    private void clearCart(String userId, OrderResult orderResult) {
        //1.要根据 userId 和 skuId一起清空
        //拿到商家集合
        ArrayList<Long> skuIds = new ArrayList<>();
        List<ShopOrderItem> shopCartOrders = orderResult.getShopCartOrders();
        shopCartOrders.forEach(shopOrderItem -> {
            //拿到商品的集合
            List<OrderItem> orderItemList = shopOrderItem.getShopCartItemDiscounts();
            //拿到skuIds集合
            List<Long> skuItem = orderItemList.stream().map(OrderItem::getSkuId).collect(Collectors.toList());
            skuIds.addAll(skuItem);
        });
        //远程调用清空购物车
        orderCartFeign.clearCart(userId, skuIds);

    }


    /**
     * 使用雪花算法生成UUID
     *
     * @return
     */
    private String createOrderNumSnowflake() {
        return snowflake.nextIdStr();
    }
}
